[GLOBAL _gdt_flush]
[GLOBAL _tss_flush]
[GLOBAL _idt_flush]
[GLOBAL _assemble_idt]

[EXTERN _create_idt_entry]
[EXTERN _isr_common]

_gdt_flush:
    ; Load new GDT and we are now in GDT memory mapping mode, no real pointers.
    mov eax, [esp+4]
    lgdt [eax]                                                                  
    
    ; 0x10 is the kernel data segment descriptor's offset in the new GDT
    mov ax, 0x10
    
    ; Point all CPU data segment registers to the kernel's data segment descriptor
    mov ds, ax                                                                  
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    
    ; 0x08 is Kernel code segment dsecriptor's offset in the new GDT
    ; Change CPU code segment register implicitly by far jumping
    jmp 0x08: .flush

.flush:
    ret


_tss_flush:
    mov ax, 0x2B
    ltr ax
    ret

%macro CREATE_ENTRY 1
    push dword 0x8E
    push dword 0x08
    push dword isr_%1
    push dword %1
    call _create_idt_entry
    add esp, 16
%endmacro

%macro ISR_STANDARD 1
[GLOBAL isr_%1]
isr_%1:
    cli
    ; Push a dummy error code
    push byte 0
    ; Push interrupt number
    push byte %1
    jmp _isr_common
%endmacro

%macro ISR_ERROR 1
[GLOBAL isr_%1]
isr_%1:
    cli
    ; Error code already pushed
    ; Push interrupt number
    push byte %1
    jmp _isr_common
%endmacro

%macro IRQ_STANDARD 1
[GLOBAL isr_%1]
isr_%1:
    cli
    push byte 0
    push byte %1
    jmp _isr_common
%endmacro

_assemble_idt:
    %assign i 0
    %rep 48
        CREATE_ENTRY i
    %assign i i+1
    %endrep
    ret

_idt_flush:
    mov eax, [esp+4]
    lidt [eax]
    ret
    
%assign i 0
%rep 8
    ISR_STANDARD i
%assign i i+1
%endrep

ISR_ERROR 8
ISR_STANDARD 9

%assign i 10
%rep 5
    ISR_ERROR i
%assign i i+1
%endrep

%assign i 15
%rep 17
    ISR_STANDARD i
%assign i i+1
%endrep

%assign i 32
%rep 16
    IRQ_STANDARD i
%assign i i+1
%endrep
